package structure;

import geography.Location;
import geography.Maze;
import geography.Orientation;

import java.util.Stack;

/**
 * A node representing a possible junction, corridor or dead-end in the {@link Maze}.
 * @author Nils Klement
 * @since 1.7
 * 
 */
public class MazeNode{
	private Location location;
	
	private MazeNode north = null;
	private MazeNode east = null;
	private MazeNode south = null;
	private MazeNode west = null;
	
	private Stack<MazeNode> paths;
	
	private boolean disconnected;
	
	/**
	 * Creates a disconnected node at the specific coordinates on the grid.
	 * @param xCoord x-coordinate
	 * @param yCoord y-coordinate
	 */
	public MazeNode(int xCoord, int yCoord){
		disconnected = true;
		
		paths = new Stack<MazeNode>();
		
		location = new Location(xCoord, yCoord);
	}
	
	public Location getLocation(){
		return location;
	}
	
	/**
	 * Returns more immediate information on whether the node has any connections to the maze at all.
	 * @return Boolean that is {@code true} if no connecting nodes are set and {@code false} otherwise.
	 */
	public boolean isDisconnected(){
		return disconnected;
	}
	
	/**
	 * Gives all possible paths from this node.
	 * @return Stack of {@link MazeNode} connected to this node.
	 */
	public Stack<MazeNode> getAllPaths(){
		return paths;
	}
	
	/**
	 * Return the node bordering this one to the {@code North}.
	 * @return {@link MazeNode} to the {@code North} or {@code null}.
	 */
	public MazeNode getNorth(){
		return north;
	}
	/**
	 * Return the node bordering this one to the {@code East}.
	 * @return {@link MazeNode} to the {@code East} or {@code null}.
	 */
	public MazeNode getEast(){
		return east;
	}
	/**
	 * Return the node bordering this one to the {@code South}.
	 * @return {@link MazeNode} to the {@code South} or {@code null}.
	 */
	public MazeNode getSouth(){
		return south;
	}
	/**
	 * Return the node bordering this one to the {@code West}.
	 * @return {@link MazeNode} to the {@code West} or {@code null}.
	 */
	public MazeNode getWest(){
		return west;
	}
	
	/**
	 * Sets the node bordering this one to the {@code North}.
	 * @param input {@link MazeNode} which this node is connected to.
	 */
	public void setNorth(MazeNode input){
		disconnected = false;
		north = input;
		paths.push(input);
	}
	/**
	 * Sets the node bordering this one to the {@code East}.
	 * @param input {@link MazeNode} which this node is connected to.
	 */
	public void setEast(MazeNode input){
		disconnected = false;
		east = input;
		paths.push(input);

	}
	/**
	 * Sets the node bordering this one to the {@code South}.
	 * @param input {@link MazeNode} which this node is connected to.
	 */
	public void setSouth(MazeNode input){
		disconnected = false;
		south = input;
		paths.push(input);
	}
	/**
	 * Sets the node bordering this one to the {@code West}.
	 * @param input {@link MazeNode} which this node is connected to.
	 */	
	public void setWest(MazeNode input){
		disconnected = false;
		west = input;
		paths.push(input);
	}
	
	/**
	 * Attempt to move in the specified {@code Direction} and return the resulting node.  
	 * @param heading {@link Orientation} in which to move.
	 * @return {@link MazeNode} bordering this one to the specified {@link Orientation} or {@code null}.
	 */
	public MazeNode goTo(Orientation heading){
		switch (heading){
			case NORTH:
				return north;
			case EAST:
				return east;
			case SOUTH:
				return south;
			case WEST:
				return west;			
		}
		return null;
	}
	
	/**
	 * Sets a connected {@link MazeNode} to this node according to the direction.
	 * @param whereFrom {@link Orientation} from the node.
	 * @param previousNode Previous {@link MazeNode}.
	 */
	public void setByOrientation(Orientation whereTo, MazeNode nextNode){
		switch (whereTo){
			case NORTH:
				setNorth(nextNode);
				break;
			case EAST:
				setEast(nextNode);
				break;
			case SOUTH:
				setSouth(nextNode);
				break;
			case WEST:
				setWest(nextNode);
				break;			
		}
	}
	
	public String toString(){
		return "node@["+location.getX()+","+location.getY()+"]";
	}
	
	public boolean isDeadEnd(){
		if(north == null && east == null && west == null && south != null){
			return true;									//south
		}
		if(north == null && east == null && south == null && west != null){
			return true;									//west
		}
		if(north == null && west == null && south == null && east != null){
			return true;									//east
		}
		if(east == null && west == null && south == null && north != null){
			return true;
		}
		return false;
	}

}